/*
 * Copyright (C) 2013 Manoel Afonso Filho
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package agimagem.context;

import agimagem.fitness.Fitness;
import agimagem.populacao.Individuo;
import agimagem.populacao.Populacao;
import agimagem.util.AGChave;
import java.util.ArrayList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * Implementação do Algoritmo Genético.
 * @author Manoel Afonso Filho
 */
public class ImgAG extends AG {

    private static Logger log = LogManager.getLogger(ImgAG.class.getName());

    /**
     * Gera a população inicial a partir do plaintext fornecido. O plaintext
     * passado será guardado para servir de referência no cálculo do fitness.
     * @return População inicial com os indivíduos derivados do plaintext.
     */
    @Override
    public Populacao gerarPopulacaoInicial(byte[] imagemOriginal) {
        return this.getInicializacao().criarPopulacao(imagemOriginal);
    }

    @Override
    public Populacao executarGeracao(Populacao p) {
        // Seleção
        Populacao matingPool = this.getSelecao().executar(p);
        Populacao offspring = new Populacao();

        // Crossover
        if (this.getCrossover() != null) {
            ArrayList<Individuo> indi = matingPool.getIndividuos();
//            Collections.sort(indi);
//            for (int i = 0; i < indi.size(); i += 2) {
//                Individuo[] filhos = this.getCrossover().executar(indi.get(i), indi.get(i + 1));
//                offspring.guardarIndividuo(filhos);
//            }
            for (int i = 0; i < indi.size(); i++) {
                Individuo[] filhos = this.getCrossover().executar(indi.get(i));
                offspring.guardarIndividuo(filhos);
            }
        }

        // Mutação
        if (this.getMutacao() != null) {
            for (int i = 0; i < offspring.getTamanhoPop(); i++) {
                Individuo mutado = this.getMutacao().executar(offspring.getIndividuoAt(i));
                offspring.setIndividuoAt(i, mutado);
            }
        }

        // Seleção de sobriventes (Replacement)
        if(this.getReplacement() != null){
            this.getReplacement().executar(p, offspring);
        }

        // Incrementa a geração
        incrementarGeracao();

        return p;
    }

    /**
     * Calcula o quão melhor o fitness da população p2 é em relação a p1.
     *
     * @param p1 População no início de uma geração.
     * @param p2 População no fim de uma geração.
     * @return Uma porcentagem referente ao aumento do fitness.
     */
    private double calcularProgresso(Populacao p1, Populacao p2) {
        double melhor1 = p1.getFittest().getFitness();
        double melhor2 = p2.getFittest().getFitness();
        double r = Math.abs(100 - ((melhor2 * 100) / melhor1));
        if (melhor1 > melhor2) {
            r *= -1;
        }
        return r;
    }

    @Override
    public byte[] decriptografar(byte[] ciphertext, AGChave chave) {
        if(this.getMutacao() != null){
            ciphertext = this.getMutacao().reverso(ciphertext, chave);
        }

        if(this.getCrossover() != null){
            ciphertext = this.getCrossover().reverso(ciphertext, chave);
        }

        if(this.getInicializacao() != null){
            ciphertext = this.getInicializacao().reverso(ciphertext, chave);
        }

        return ciphertext;
    }
}
